
use windows::core::Error as WinError;

use super::{ipc_receiver::OsIpcReceiver, share_memory::OsIpcSharedMemory};

use super::{enums::WinIpcError, ipc_sender::OsIpcSender, utils, win_handle::WinHandle};



pub fn channel() -> Result<(OsIpcSender, OsIpcReceiver), WinError> {
    let pipe_id = utils::make_pipe_id();
    let pipe_name = utils::make_pipe_name(&pipe_id);

    let receiver = OsIpcReceiver::new_named(&pipe_name)?;
    let sender = OsIpcSender::connect_named(&pipe_name)?;

    Ok((sender, receiver))
}


pub struct OsIpcOneShotServer {
    receiver: OsIpcReceiver,
}

impl OsIpcOneShotServer {
    pub fn new() -> Result<(OsIpcOneShotServer, String), WinError> {
        let pipe_id = super::utils::make_pipe_id();
        let pipe_name = super::utils::make_pipe_name(&pipe_id);
        let receiver = OsIpcReceiver::new_named(&pipe_name)?;
        Ok((
            OsIpcOneShotServer { receiver: receiver },
            pipe_id.to_string(),
        ))
    }

    pub fn accept(
        self,
    ) -> Result<
        (
            OsIpcReceiver,
            Vec<u8>,
            Vec<OsOpaqueIpcChannel>,
            Vec<OsIpcSharedMemory>,
        ),
        WinIpcError,
    > {
        let receiver = self.receiver;
        receiver.accept()?;
        let (data, channels, shmems) = receiver.recv()?;
        Ok((receiver, data, channels, shmems))
    }
}

pub enum OsIpcChannel {
    Sender(OsIpcSender),
    Receiver(OsIpcReceiver),
}

#[derive(Debug)]
#[cfg_attr(feature = "windows-shared-memory-equality", derive(PartialEq))]
pub struct OsOpaqueIpcChannel {
    handle: WinHandle,
}

impl Drop for OsOpaqueIpcChannel {
    fn drop(&mut self) {
        // Make sure we don't leak!
        //
        // The `OsOpaqueIpcChannel` objects should always be used,
        // i.e. converted with `to_sender()` or `to_receiver()` --
        // so the value should already be unset before the object gets dropped.
        debug_assert!(!self.handle.is_valid());
    }
}

impl OsOpaqueIpcChannel {
    pub(super) fn new(handle: WinHandle) -> OsOpaqueIpcChannel {
        OsOpaqueIpcChannel { handle: handle }
    }

    pub fn to_receiver(&mut self) -> OsIpcReceiver {
        OsIpcReceiver::from_handle(self.handle.take())
    }

    pub fn to_sender(&mut self) -> OsIpcSender {
        OsIpcSender::from_handle(self.handle.take())
    }
}




pub enum OsIpcSelectionResult {
    DataReceived(
        u64,
        Vec<u8>,
        Vec<OsOpaqueIpcChannel>,
        Vec<OsIpcSharedMemory>,
    ),
    ChannelClosed(u64),
}

impl OsIpcSelectionResult {
    pub fn unwrap(
        self,
    ) -> (
        u64,
        Vec<u8>,
        Vec<OsOpaqueIpcChannel>,
        Vec<OsIpcSharedMemory>,
    ) {
        match self {
            OsIpcSelectionResult::DataReceived(id, data, channels, shared_memory_regions) => {
                (id, data, channels, shared_memory_regions)
            },
            OsIpcSelectionResult::ChannelClosed(id) => {
                panic!(
                    "OsIpcSelectionResult::unwrap(): receiver ID {} was closed!",
                    id
                )
            },
        }
    }
}
